package spring.cloud.tasks.tasks_manager.waitingPreAssignTasks;

import com.alibaba.fastjson2.JSON;
import spring.cloud.tasks.tasks_manager.schedule.TaskInstanceInfo;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 滑动时间任务队列
 *
 * @author huihui
 * map+PriorityQueue模式
 */
public class SlidingTimeTaskBucketQueue {
    /**
     * SlidingTimeRangeTaskInfoList 重写key:rang Comparable hashCode/equals方法。主要是进行queue快速查找。该方法必须在加锁的情况下能够得到最优的性能以及数据的完整性。
     */
    private final ConcurrentHashMap<SlidingTimeTaskBucket, SlidingTimeTaskBucket> map = new ConcurrentHashMap<SlidingTimeTaskBucket, SlidingTimeTaskBucket>();
    private final PriorityQueue<SlidingTimeTaskBucket> queue = new PriorityQueue<SlidingTimeTaskBucket>(new Comparator<SlidingTimeTaskBucket>() {
        @Override
        public int compare(SlidingTimeTaskBucket o1, SlidingTimeTaskBucket o2) {
            long o1nextFireDate = o1.startTime;
            long o2nextFireDate = o2.startTime;
            if (o1nextFireDate == o2nextFireDate) {
                return 0;
            }
            return o1nextFireDate < o2nextFireDate ? -1 : 1;
        }
    });


    public void addTask(List<TaskInstanceInfo> taskInfoList) {
        //
        for (TaskInstanceInfo taskInfo : taskInfoList) {
            long nextFireDate = taskInfo.getNextFireDate().getTime();
            long baseNum = 1000L * 60;
            long divValue = nextFireDate / baseNum;
            long modValue = nextFireDate % baseNum;
            if (modValue == 0) {
                taskInfo.setPreAssignTimeStamp(nextFireDate - baseNum + 1);

            } else {
                taskInfo.setPreAssignTimeStamp((divValue * baseNum) + 1);

            }
        }
        ConcurrentHashMap<SlidingTimeTaskBucket, SlidingTimeTaskBucket> map = new ConcurrentHashMap<SlidingTimeTaskBucket, SlidingTimeTaskBucket>();
        for (TaskInstanceInfo taskInfo : taskInfoList) {
            SlidingTimeTaskBucket slidingTimeRangeAndTaskInfoList = new SlidingTimeTaskBucket();
            slidingTimeRangeAndTaskInfoList.setStartTime(taskInfo.getPreAssignTimeStamp());

            if (map.containsKey(slidingTimeRangeAndTaskInfoList)) {
                map.get(slidingTimeRangeAndTaskInfoList).getTaskInfoList().add(taskInfo);
            } else {
                map.put(slidingTimeRangeAndTaskInfoList, slidingTimeRangeAndTaskInfoList);
                slidingTimeRangeAndTaskInfoList.getTaskInfoList().add(taskInfo);
            }
        }
        /**
         * 加锁的情况下 性能达到o1
         */
        synchronized (this) {
            for (Map.Entry<SlidingTimeTaskBucket, SlidingTimeTaskBucket> entry : map.entrySet()) {
                if (this.map.containsKey(entry.getKey())) {
                    this.map.get(entry.getKey()).getTaskInfoList().add(entry.getKey().getTaskInfoList());
                } else {
                    this.map.put(entry.getKey(), entry.getKey());
                    this.queue.add(entry.getKey());
                }
            }
        }
    }

    public List<TaskInstanceInfo> getTaskInstanceInfoList() {
        List<TaskInstanceInfo> taskInfoList = new ArrayList<TaskInstanceInfo>();
        List<SlidingTimeTaskBucket> list = new ArrayList<SlidingTimeTaskBucket>();
        long nowDateTime = System.currentTimeMillis();
        synchronized (this) {
            while (true) {
                SlidingTimeTaskBucket slidingTimeTaskBucket = queue.peek();//返回第一个但是不删除
                if (slidingTimeTaskBucket == null) {
                    break;
                }
                long startTime = slidingTimeTaskBucket.startTime;
                long endTime = slidingTimeTaskBucket.endTime;
                boolean match = (endTime < nowDateTime) || (startTime <= nowDateTime && nowDateTime <= endTime);
                if (match) {
                    slidingTimeTaskBucket = queue.poll();//返回第一个+删除(外面进行任务取消可以清空链表内容 但是不能清空引用)
                    list.add(slidingTimeTaskBucket);
                    this.map.remove(slidingTimeTaskBucket);
                } else {
                    break;
                }
            }
        }
        for (SlidingTimeTaskBucket slidingTimeRangeAndTaskInfoList : list) {
            taskInfoList.addAll(slidingTimeRangeAndTaskInfoList.taskInfoList);
        }
        return taskInfoList;
    }

    public static void main(String[] args) throws InterruptedException {
        SlidingTimeTaskBucketQueue slidingTimeTaskQueue = new SlidingTimeTaskBucketQueue();
        List<TaskInstanceInfo> taskInfoList = new ArrayList<>();
        TaskInstanceInfo taskInfo = new TaskInstanceInfo();
        taskInfo.setNextFireDate(new Date());
        taskInfoList.add(taskInfo);
        taskInfoList.add(taskInfo);
        taskInfoList.add(taskInfo);
        slidingTimeTaskQueue.addTask(taskInfoList);

        System.out.println(JSON.toJSON(slidingTimeTaskQueue.getTaskInstanceInfoList()));
    }
}
